/** @file    CronJob.cpp
 *  @author  Alessandro Polo
 *  @version $Id: CronJob.cpp 2829 2010-08-02 20:45:47Z alex $
 *  @brief
 * File containing methods for the wosh::CronJob class.
 * The header for this class can be found in CronJob.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include "CronJob.h"

 #include <core/ObjectAllocator.h>
 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/Utilities.h>

 #include <interfaces/services/TaskManager.h>

 #include <stdlib.h>
 #include <time.h>


using namespace std;
namespace wosh {
 namespace automations {

 WOSH_REGISTER(wosh::automations::CronJob, "wosh::automations::Automation", _CronJob_VERSION, _static_CronJob )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

CronJob::CronJob( )
	: Automation() {
	clear();
 }

CronJob::CronJob( const CronJob& m  )
	: Automation(m) {
	clear();
	*this = m;
 }

CronJob::~CronJob() {
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT CronJob::init() {
	WRESULT ret = Automation::init();
	updatedTime(true);
	return ret;
 }

WRESULT CronJob::deinit() {
	WRESULT ret = WRET_OK;
	schedulerControl(false);
	ret += Automation::deinit();
	return ret;
 }

WRESULT CronJob::execute( const Message* trigger_message, const MessageFilter* trigger_filter ) {
	this->effects.transactionBeginRead();
	WRESULT ret = execute_(trigger_message, trigger_filter, 0);
	this->effects.transactionEnd();
	updatedTime(true);
	return ret;
 }

void CronJob::schedulerControl( bool enable ) {
	Log(LOG_INFO, ":schedulerControl(%d) : NextInterrupt %"PRId64"", (int)enable, this->nextInterrupt );
	List* args = new List(2);
	args->push_back<Variant>(this->uid);
	if ( enable )
		args->push_back<Variant>(this->nextInterrupt);
	else
		args->push_back<Variant>(0);
	Message* msgRequest = new Message(new Request(_TaskManager_METHOD_schedule, args));
	msgRequest->setDestination( Object::getURI().getParent() );
	postMessage(msgRequest);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CronJob::clear() {
	Automation::clear();
	this->year = 0;
	this->month = 0;
	this->day = 0;
	this->hour = 0;
	this->min = 0;
	this->sec = 0;
	this->nextInterrupt = 0;
 }

void CronJob::setDate( int year_v, int month_v, int day_v ) {
	Log(LOG_INFO, ":setDate(%d, %d, %d)", year_v, month_v, day_v );
	this->year = year_v;
	this->month = month_v;
	this->day = day_v;
	updatedTime();
 }

void CronJob::setTime( int hour_v, int min_v, int sec_v ) {
	Log(LOG_INFO, ":setTime(%d, %d, %d)", hour_v, min_v, sec_v );
	this->hour = hour_v;
	this->min = min_v;
	this->sec = sec_v;
	updatedTime();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CronJob::updatedTime( bool schedule_commit ) {
	this->nextInterrupt = 0;
	time_t rawtime;
	struct tm* timeinfo;
	time ( &rawtime );
	timeinfo = localtime ( &rawtime );
	if ( !isYearly() )
		timeinfo->tm_year = this->year - 1900;
	if ( !isMonthly() )
		timeinfo->tm_mon = this->month - 1;
	if ( !isDaily() )
		timeinfo->tm_mday = this->day;
	if ( !isHourly() )
		timeinfo->tm_hour = this->hour;
	if ( !isMinutely() )
		timeinfo->tm_min = this->min;
	rawtime = mktime( timeinfo );
	this->nextInterrupt = rawtime;
	Log(LOG_VERBOSE, ":updatedTime(%d) NextInterrupt %"PRId64"", (int)schedule_commit, this->nextInterrupt );
	Properties.transactionBeginWrite();
	Properties.update( _CronJob_KEY_NextInterrupt, Variant_DT(this->nextInterrupt) );
	Properties.transactionEnd();
	if ( schedule_commit )
		schedulerControl(true);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CronJob::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source ) {
	if ( property_current.getKey() == _CronJob_KEY_year ) {
		int _year = value_proposed.toInteger(this->year);
		if ( _year < -1 ) return false;
		this->year = _year;
		updatedTime(true);
		return true;
	 }
	else if ( property_current.getKey() == _CronJob_KEY_month ) {
		int _month = value_proposed.toInteger(this->month);
		if ( _month <= -1 || _month > 12 ) return false;
		this->month = _month;
		updatedTime(true);
		return true;
	 }
	else if ( property_current.getKey() == _CronJob_KEY_day ) {
		int _day = value_proposed.toInteger(this->day);
		if ( _day <= -1 || _day > 31 ) return false;
		this->day = _day;
		updatedTime(true);
		return true;
	 }
	else if ( property_current.getKey() == _CronJob_KEY_hour ) {
		int _hour = value_proposed.toInteger(this->hour);
		if ( _hour < -1 || _hour >= 24 ) return false;
		this->hour = _hour;
		updatedTime(true);
		return true;
	 }
	else if ( property_current.getKey() == _CronJob_KEY_min ) {
		int _min = value_proposed.toInteger(this->min);
		if ( _min < -1 || _min >= 60 ) return false;
		this->min = _min;
		updatedTime(true);
		return true;
	 }
	else if ( property_current.getKey() == _CronJob_KEY_sec ) {
		int _sec = value_proposed.toInteger(this->sec);
		if ( _sec < -1 || _sec >= 60 ) return false;
		this->sec = _sec;
		updatedTime(true);
		return true;
	 }
	return Automation::updatingProperty(do_update, value_proposed, property_current, source);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

CronJob& CronJob::operator=(const CronJob& m){
	if (this == &m) return *this; // same object?
	(Automation&)*this = (const Automation&)m;
	this->nextInterrupt = m.nextInterrupt;
	this->year = m.year;
	this->month = m.month;
	this->day = m.day;
	this->hour = m.hour;
	this->min = m.min;
	this->sec = m.sec;
	return *this;
 }
 
bool CronJob::operator==(const CronJob &other) const {
	if ( this->year != other.year ) return false;
	if ( this->month != other.month ) return false;
	if ( this->day != other.day ) return false;
	if ( this->hour != other.hour ) return false;
	if ( this->min != other.min ) return false;
	if ( this->sec != other.sec ) return false;
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// DEBUG & INFO
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION

std::string CronJob::getDate() const {
	char buffer [63];
	memset(buffer, '\0', 63);
/// #if defined(_MSC_VER) && !defined(_OS_WINCE)
/// 	sprintf_s(buffer, "%04d-%02d-%02d", this->year, this->month, this->day);
/// #else
	sprintf(buffer, "%04d-%02d-%02d", this->year, this->month, this->day);
/// #endif
	return string(buffer);
 }

std::string CronJob::getTime() const {
	char buffer [63];
	memset(buffer, '\0', 63);
/// #if defined(_MSC_VER) && !defined(_OS_WINCE)
/// 	sprintf_s(buffer, "%02d:%02d", this->hour, this->min);
/// #else
	sprintf(buffer, "%02d:%02d", this->hour, this->min);
/// #endif
	return string(buffer);
 }

bool CronJob::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = Automation::updateHash(hash_context);
	ret = hash_context->update( this->year ) & ret;
	ret = hash_context->update( this->month ) & ret;
	ret = hash_context->update( this->day ) & ret;
	ret = hash_context->update( this->hour ) & ret;
	ret = hash_context->update( this->min ) & ret;
	ret = hash_context->update( this->sec ) & ret;
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// SERIALIZATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace automations
}; // namespace wosh
